05-06 开发工具
05 嵌入式C语言编译器
GCC与gcc有什么不同?
- GCC(GNU Compiler Collection)
- GNU编译器集合,包含众多语言的编译器:C,C++,Java,D,Objective-C,etc.
- gcc:特指GCC中的C语言编译器
GCC VS. 嵌入式
- 多数嵌入式操作系统都基于GCC进行源码编译:Linux,VxWorks,Android,etc.
实际开发中的使用
- 内核开发:gcc
- 应用开发:gcc/g++/gdc
什么是交叉编译
- 背景:嵌入式设备往往资源受限,不可能在嵌入式上直接对处理器进行编程。
- 解决方案:在开发主机(PC)上对源码进行编译,最终生成目标主机(嵌入式设备)的可执行程序。
gcc是如何进行交叉编译的?
- 配置目标主机的编译工具链(如:arm-linux)
- 配置工具链的具体版本
- 根据具体的目标代码选择相应的工具链版本
- 正确使用关于硬件体系结构的特殊编译选项
案例:大型企业嵌入式开发环境
初识编译器
你不知道的事...
拓展问题:如何理解"多语言混合开发"?
多语言混合开发方式一
行业案例:.net framework
多语言混合开发方式二
行业案例: QQ
多语言混合开发方式三
行业案例:Eclipse
gcc关键编译选项
-
预处理指令:gcc -E file.c -o file.i
-
编译指令:gcc -S file.i -o file.s
-
汇编指令:gcc -c file.s -o file.o
-
生成映射文件:gcc -WI,-Map=test.map file.c
-
宏定义:gcc -D'TEST="Test"' file.c
-
获取系统头文件路径:gcc -v file.c
-
生成依赖关系
- 获取目标的完整依赖关系:gcc -M test.c
- 获取目标的部分依赖关系:gcc -MM test.c
-
指定库文件及库文件搜索路径
- -L选项:指定库文件的搜索路径
- -l选项:指定库文件
-
gcc test.c -L. -lfunc
06 开发中的辅助工具
什么是开发环境
- 构建环境:代码编写,程序编译,版本控制(可选)
- 调试环境:用于定位问题的辅助工具集
- 测试环境:用于验证目标程序是否满足用户的显性需求和隐性需求
嵌入式开发中的时间分配
- 代码编写即目标构建(20%)
- 测试,调试,bug修复(80%)
问题:如何提高开发效率?
工欲善其事,必先利其器!
GNU为GCC编译器提供了配套的辅助工具集(Binutils),http://www.gnu.org/software/binutils。
工具名 | 功能简介 |
---|---|
addr2line | 将代码地址转换为对应的程序行号 |
strip | 剔除可执行程序中的调试信息 |
ar | 将目标文件打包成为静态库 |
nm | 列出目标文件中的符号及对应地址 |
objdump | 查看程序段信息及反汇编 |
size | 查看目标文件中的段大小 |
strings | 查看目标文件中的字符串 |
readelf | 用于读取 ELF(Executable and Linkable Format)格式文件的详细信息 |
addr2line
- 将指定地址转换为对应的文件名和行号
- 常用于分析和定位内存访问错误的问题
void func() {
/*oops!!*/
*g_pointer = (int)"Hello World";
return;
}
addr2line示例:定位0地址访问
- 开启core dump选项:ulimit -c unlimited
- 运行程序,并生成崩溃时的core文件,执行导致程序崩溃的测试用例
- 读取core文件,获取IP寄存器的值(0x08048000),dmesg core
- 使用addr2line定位代码行,addr2line 0x08048000 -f -e test.out
strip
- 剔除程序文件中的调试信息,减少目标程序的大小
- 一般在程序发布前都需要将调试信息剔除
- 过多的调试信息可能影响程序的执行效率 strip test.out
注意事项
- 几乎所有的调试辅助工具都依赖于目标文件中的调试信息
- 调试信息的运用能够快速定位问题
- 使用gcc编译程序时使用-g选项生成调试信息
- 发布程序时再考虑是否使用strip剔除调试信息
ar
- 打包目标文件:
ar crs libname.a x.o y.o
- 解压目标文件:
ar x libname.a
nm
- 列出目标文件中的标识符(变量名,函数名)
- 输出结果由三部分组成:(地址,断,标识符)
示例:
- 段标识说明
段标识 | 说明 |
---|---|
A | 地址值在链接过程中不会发生改变 |
B或b | 标识符位于未初始化数据段(.bss) |
C | 未定义存储段的标识符,链接时决定段位置 |
D或d | 标识符位于数据段(.data) |
N | 调试专用标识符 |
R或r | 标识符位于只读存储区(.rdata) |
T或t | 标识符位于代码段(.text) |
U | 未定义的标识符 |
objdump
- 反汇编目标文件,查看汇编到源码的映射
- objdump -d func.o
- objdump -S func.o
- 查找目标文件中的详细段信息
- objdump -h test.out
objdump -h的输出说明
符号 | 说明 |
---|---|
Idx | 段下标 |
Name | 段标识符(名字) |
Size | 段所占空间的大小 |
VMA | 段起始位置的虚存地址 |
LMA | 段在存储空间中的加载地址 |
File off | 段在目标文件中的相对位置 |
Algn | 段的边界对齐字节数 |
size
获取目标文件中的所有段大小
- size test.out
strings
获取目标文件中的所有字符串常量
- strings test.out
readelf
使用readelf查找程序所需的动态库:
readelf --dynamic ./exetuable | grep NEEDED
输出如下:
0x0000000000000001 (NEEDED) Shared library: [librwp2p.so]
0x0000000000000001 (NEEDED) Shared library: [libzlog.so]
0x0000000000000001 (NEEDED) Shared library: [libcrypto.so.1.0.0]
0x0000000000000001 (NEEDED) Shared library: [libssl.so.1.0.0]
......
0x0000000000000001 (NEEDED) Shared library: [libm.so.6]
0x0000000000000001 (NEEDED) Shared library: [libgcc_s.so.1]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]